package com.example.Utils;

import com.example.Annotation.AutoWired;
import com.example.Annotation.Bean;
import com.example.MyReflection.Reflect.ApplicationContext;
import com.sun.org.apache.bcel.internal.generic.LoadClass;

import java.io.File;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

/**
 * @author cc
 * @date 2021/7/31 10:37
 * @Description
 */

public class ScanUtils {
   private static String filePath;
   private static HashMap<Class,Object> map = new HashMap<>();
    public static HashMap<Class,Object> load(){
        /**
         * 使用 loadClass() 方法获得的 Class 对象只完成了类加载过程中的第一步：加载，后续的操作均未进行。
         * 使用 Class.forName() 方法获得 Class 对象是已经执行完初始化的了
         * @author sosmcc
         * @param
         */
        //拿到这个项目下的工程目录 结果为:/D:/java/workshop/ReflectionMaven/target/classes/ 注意：是编译后的class文件
        filePath = ApplicationContext.class.getClassLoader().getResource("").getFile();
//        return LoadFile(new File(filePath));

        //只装配有Annotation注解的类
        HashMap<Class, Object> classObjectHashMap = SetBeanAnnoation(new File(filePath));
        //自动装配
        assembleObject();
        return classObjectHashMap;

    }

    //递归拿文件
    public static  HashMap<Class,Object> LoadFile(File fileSource){
        if (fileSource.isDirectory()){
            //此为拿到classes下面一层的路径。一个为：
            // D:\java\workshop\ReflectionMaven\target\classes\com
            // 另外一个为：
            //D:\java\workshop\ReflectionMaven\target\classes\META-INF
            File[] childrenFiles = fileSource.listFiles(); //listfiles表示的是获取所有路径
            if (childrenFiles==null||childrenFiles.length==0){
                return null;
            }
            //如果childrenFiles底下还有子目录，继续遍历
            for (File child : childrenFiles) {
                //判断子目录是否还是目录，是就进行递归，把所有的都遍历出来
                if (child.isDirectory()){
                    LoadFile(child);
                }else {
                    //说明该目录下，这个不是目录，是文件，现在需要把该文件转换成类；
                    String pathClassName = child.getAbsolutePath().substring(filePath.length()-1);
                    //对class文件进行处理
                    if (pathClassName.contains(".class")){ //判断是否为class文件
                        //去掉.class尾缀，并把"\"转换成. //变成如：com.example.Annotation.Bean
                        String classname = pathClassName.replaceAll("\\\\", ".").replace(".class", "");

                        //通过forname (forname用法 ：CLass.forname("包的路径"))获取类；
                        try {
                            Class<?> aClass = Class.forName(classname);
                            //把接口全部放到map中的key值中，非接口放入value中；
                            if (!aClass.isInterface()){//这个判断说明该类不是接口，是类
                                Object o = aClass.newInstance();//进行实例化
                                map.put(aClass,o);
                            }
                        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return map;
    }

    public static  HashMap<Class,Object> SetBeanAnnoation(File fileSource){
        if (fileSource.isDirectory()){
            //此为拿到classes下面一层的路径。一个为：
            // D:\java\workshop\ReflectionMaven\target\classes\com
            // 另外一个为：
            //D:\java\workshop\ReflectionMaven\target\classes\META-INF
            File[] childrenFiles = fileSource.listFiles(); //listfiles表示的是获取所有路径
            if (childrenFiles==null||childrenFiles.length==0){
                return null;
            }
            //如果childrenFiles底下还有子目录，继续遍历
            for (File child : childrenFiles) {
                //判断子目录是否还是目录，是就进行递归，把所有的都遍历出来
                if (child.isDirectory()){
                    SetBeanAnnoation(child);
                }else {
                    //说明该目录下，这个不是目录，是文件，现在需要把该文件转换成类；
                    String pathClassName = child.getAbsolutePath().substring(filePath.length()-1);
                    //对class文件进行处理
                    if (pathClassName.contains(".class")){ //判断是否为class文件
                        //去掉.class尾缀，并把"\"转换成. //变成如：com.example.Annotation.Bean
                        String classname = pathClassName.replaceAll("\\\\", ".").replace(".class", "");

                        //通过forname (forname用法 ：CLass.forname("包的路径"))获取类；
                        try {
                            Class<?> aClass = Class.forName(classname);
                            //把接口全部放到map中的key值中，非接口放入value中；
                            if (!aClass.isInterface()){//这个判断说明该类不是接口，是类
                                //查看这个类里面是否有bean注解
                                Bean annotation = aClass.getAnnotation(Bean.class);
                                if (annotation!=null){
                                    Object o = aClass.newInstance();//进行实例化
                                    map.put(aClass.getInterfaces()[0],o);
                                }
                            }
                        } catch (ClassNotFoundException | InstantiationException | IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
        return map;
    }

    //给所有字段赋值
    public static  void assembleObject(){
        for (Map.Entry<Class,Object> entry : map.entrySet()){
            //拿到工厂的每一个对象
            Object obj = entry.getValue();
            //相当于通过类名获取class
            Class<?> aClass = obj.getClass();
            //获取每个对象的字段 即属性
            Field[] declaredFields = aClass.getDeclaredFields();
            for (Field declaredField : declaredFields) {
                AutoWired annotation = declaredField.getAnnotation(AutoWired.class);
                if (annotation==null){
                    //权限打开
                    declaredField.setAccessible(true);
                    //把字段赋值给对象 类
                    try {
                        System.out.println(declaredField.getType());
                        //获取属性的属性值并修改属性值  (obj 为类的对象； value 表示要修改的值 )
                        /**
                         * 假如obj 为person类
                         * 其中有 private String name
                         *       private int  age
                         *   先获取  Filed f1= person.getclass.getDeclaredField("name")
                         *   那么就可以通过 f1.set 进行改变
                         *   fi.set(person,"改变名字类型"); 第一个参数是想要改变的类型 第二个参数是改变的值
                         * @author sosmcc
                         * @param
                         */
                        declaredField.set(obj,map.get(declaredField.getType()));//declaredField.getType 获取这个类的类型是int还是string还是Object
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}
